LWIP 修改ip TCP发送 连接状态 您所在的位置:网站首页 lwip tcp_write LWIP 修改ip TCP发送 连接状态

LWIP 修改ip TCP发送 连接状态

2023-04-18 12:39| 来源: 网络整理| 查看: 265

我使用TI芯片也有三年时间了,期间开发过低功耗无线传感器、无线转以太网网关等设备

现在讲一下用LM3S9B90芯片开发的无线转以太网网关设备中遇到的一些问题吧!

以太网用的是LWIP协议,这也是我第一次用32位机、第一次用ARM内核芯片、也是第一次与LWIP接触;

一开始我没有什么信心会做好,还好在TI例程及技术支持的帮助下,半年内将产品设计出来,并且相当稳定的运行。

这其间遇到最大的问题是TCP运行的问题:(前两个还是小问题,最后一个才是问题致命的所在)

   问题一,IP地址分配的问题;当时利用例程的办法,调用lwIPInit,和lwIPLocalIPAddrGet的办法获取IP地址,如果当时没接网线,或者路由器没有开启DHCP功能怎么办?

  最开始我采用重新调用以上函数的办法,进行处理,发现系统会复位;可见这个办法行不通,

  后来通过对LWIP的认识发现可以通过修改IP地址的方式,调用lwIPNetworkConfigChange函数,将IP地址设置成上次分配的IP,这样一来,只要设备能正常使用过一次,以后都不会有问题了!

  问题二,调用tcp_output函数,为了提高发送速度,在每次调用tcp_write函数前,先调用一次tcp_output函数;加了这个函数后, 短时间内使用是不会有问题的,但时间长了以后,发现系统也会复位;经过一系列的查找,终于锁定是tcp_output函数的问题,取消后,系统不再复位了

  问题三,网络异常的处理;在正常的使用过程中,会遇到设备端网线断开、服务器网线断开、服务器软件关闭、服务器电脑死机等等情况,其它几种都比较好判别,唯独服务器网线断开这种情况不能区分;

   服务器网线断开(设备与路由器链接是好的)的时候,设备不会得到异常报告,也不会有PHY寄存器标志,只能通过给服务器发送数据时,判断是否有发送完成回调函数;如果没有回调函数,服务器链路一定有问题。

   一旦出现服务器网线断开的情况,设备的LWIP中的pcb->state一直处理链接成功的状态,而服务器在一定时间后会删除这条链接,也就是 说,超过一定的时间后,设备与服务器之间的链接状态是不一样的;同时,设备一直处于虚假链接,不会重新与服务器建立链接;

   最后导致,服务器与设备是永远也不可能建立链接。

   这个问题困扰了我们好长一段时间,而且是在客户的使用中才发现,客户的压力可想而知。

   还好,经过一段时间的排查,终于找到问题的所在、以及解决问题的办法:

   效仿服务器的办法,在经过一段超时后,主动断开与服务器(确切来说是路由器)的链接,给服务器发送close命令;发完后,pcb进入 FIN_WAIT_1状态和FIN_WAIT_2状态,如果期间链接上,服务返回ACK确认关闭链接, 否则超时后,设备自动进入CLOSE状态;最终达到两端的链接状态一致;一旦链路恢复正常,设备与服务器就能再次正常建立链接。这个问题总算是解决了。

   设备运行到现在已经快半年了,目前为止,还没有发现什么问题。

lwip源码分析 http://wenku.baidu.com/view/84a478a2284ac850ad024263.html

lwip状态

状态:描述   CLOSED:无连接是活动的或正在进行   LISTEN:服务器在等待进入呼叫   SYN_RECV:一个连接请求已经到达,等待确认   SYN_SENT:应用已经开始,打开一个连接   ESTABLISHED:正常数据传输状态   FIN_WAIT1:应用说它已经完成   FIN_WAIT2:另一边已同意释放   ITMED_WAIT:等待所有分组死掉   CLOSING:两边同时尝试关闭   TIME_WAIT:另一边已初始化一个释放   LAST_ACK:等待所有分组死掉

这次主要讲解tcp函数的发送部分。 我们的流程是tcp_write->tcp_enqueue->tcp_output. 1.         tcp_write函数 这个函数很简单,首先判断此时的pcb是否进入了可发送数据的状态,这些状态有ESTABLISHED, CLOSE_WAIT, SYN_SENT, SYN_RECD,(还有一些在状态机里面见到的状态,如LISTEN,FIN_WAIT1,FIN_WAIT2,CLOSED,CLOSING等则要么处于连接还未建立,要么是连接已关闭的状态,顶多再能发个ack包,不能再发数据了)如果是处于这些有效状态,则调用tcp_enqueue函数。   2.        tcp_enqueue函数 tcp_enqueue通过把上层需要发送的数据包放在队列中,最后一起调用tcp_output来提高效率。看一下参数: tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, u8_t flags, u8_t apiflags, u8_t optflags) pcb:当前连接的pcb,arg:需要发送的数据,len:数据长度,flags:tcp首部的标志位。apiflags:TCP_WRITE_FLAG_COPY(说明空间需要分配并且拷贝至pbuf,否则则说明数据来自静态的存储器,比如rom,不需要拷贝)TCP_WRITE_FLAG_MORE。   看代码流程: A.首先保存一些变量,比如此次传输数据的长度,要传输的数据的地址 B. 将所要传输的数据切割成一个个tcp_seg结构体,最大长度不超过一个mss,如果小于就是一个tcp_seg。很奇怪,第一个seg是存放在queue指针的队列上,其余的是存放在useg指针的队列上,并且useg始终指向最后一个seg结构体。 C. 完成了数据切割,我们现在需要把这些seg加入到pcb->unsend队列中。但是这段代码很奇怪,不知道是我看错了还是有bug?但理论上不会有这么明显的bug啊。   3.        tcp_output函数 在tcp_enqueue函数中已经把我们要发送的数据放在了pcb的unsend链表上了。接下来我们就是要将这些数据发送出去。看流程: A.      首先过滤flags标志位为立即响应并且没有携带数据的调用,直接tcp_send_empty_ack(pcb);(因为不是每个对tcp_output的调用都是经过tcp_enqueue) B.       利用while循环对unsend链表上的seg一个个发送或跳过。 a)        跳过:对每个seg包会首先用nagle算法做一下检测,如果满足nagle条件,则会跳过这个数据包不做任何操作,留置合并一起发送。具体的条件可参考tcp概念部分的文章。 b)        发送:对于一般的seg包,则先置ack标志位,然后调用tcp_output_segment(seg, pcb);发送。   4.        关于tcp_output中的tcp_send_empty_ack(pcb); a)        这里面的操作比较简单,主要是建立pbuf,然后建立tcp的首部与pbuf的联系,tcphdr = p->payload,然后填充tcphdr,传递给ip层。(调用ip_output函数) 5.        关于tcp_output中的tcp_output_segment(seg, pcb); a)        同tcp_send_empty_ack(pcb);类似,不是很复杂。

原帖地址:http://www.deyisupport.com.sixxs.org/question_answer/f/23/t/5240.aspx?PageIndex=8作者:hasten lin on 2012-1-12 0:15



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有